function Vue(options = {}) {
  this.$options = options
  console.log(this.$options)  //{el: "#app", data: {a:1}}
  var data = this._data = this.$options.data
  console.log(data)   //{a:1}

  observe(data)

  // 数据代理
  // this代理this._data
  for (let key in data) {
    Object.defineProperty(this, key, {
      enumerable: true,
      get() {
        return this._data[key]
      },
      set(newVal) {
        this._data[key] = newVal
      }
    })
  }
  initComputed.call(this)
  compile(options.el, this)
}

function initComputed() {
  let vm = this
  let computed = this.$options.computed
  console.log(computed)   //{hello:f}  
  console.log(Object.keys(computed))   //[0:"hello"]
  Object.keys(computed).forEach(function(key) {    //computed具有缓存功能
    // Object.defineProperty方法映射到实例上
    Object.defineProperty(vm, key, {
      get:typeof computed[key] === 'function' ? computed[key] : computed[key].get,
    })
  })
}

function compile(el, vm) {
  // el为替换的范围
  vm.$el = document.querySelector(el)
  let fragment = document.createDocumentFragment()

  while (child = vm.$el.firstChild) {
    // 将内容移到内存中
    fragment.appendChild(child)
  }

  console.log(fragment)  //<p>{{a.a}}</p><div>{{b}}</div>
  // console.log(a)   //<div id="app"><p>{{a.a}}</p><div>{{b}}</div></div>


  replace(fragment)

  function replace(fragment) {
    // 标签替换
    console.log(fragment.childNodes)   //元素中的空格被视为文本，而文本被视为节点。 NodeList(4)[text, p, div, text]
    console.log(Array.from(fragment.childNodes))   // [text, p, div, text]
    // Array.from（）将类数组转为数组
    Array.from(fragment.childNodes).forEach(function (item) {
      let text = item.textContent   //{{a.a}}   {{b}}
      let reg = /\{\{(.*)\}\}/
      if (item.nodeType === 3 && reg.test(text)) {
        console.log(RegExp.$1)   // a.a   b
        let arr = RegExp.$1.split('.')
        console.log(arr)   //[a,a]  [b]
        let val = vm
        arr.forEach(function (k) {
          console.log(k)
          val = vm[k]
          console.log(val)
        })
        // 替换开始
        new Watcher(vm, RegExp.$1, function (newVal) {   //监听函数
          // 替换新值
          item.textContent = text.replace(/\{\{(.*)\}\}/, newVal)
        })

        item.textContent = text.replace(/\{\{(.*)\}\}/, val)
      }
      if (item.nodeType === 1) {
        // item.nodeType === 1 元素节点    [object HTMLInputElement]
        let nodeAttrs = item.attributes
        // NamedNodeMap {0: type, 1: v-model, type: type, v-model: v-model, length: 2}  类数组
        // console.log(nodeAttrs)  
        // Array.from(nodeAttrs)将类数组转化为真正的数组
        Array.from(nodeAttrs).forEach(function (attr) {
          // console.log(attr)  //type="text"  v-model="b"
          let name = attr.name     //键  type   v-model 
          let value = attr.value   //值  text   b
          if (name.indexOf('v-') == 0) {
            // 键中包含'v-'字符串
            console.log(item + "---------------------------")   //HTMLInputElement
            console.log(vm[value])  //是b
            console.log(vm + '????????????????')
            item.value = vm[value]  //将input元素的value值赋值为绑定的值
          }
          // 替换开始
          new Watcher(vm, value, function (newVal) {   //监听函数
            // 替换新值  Watcher监听数据的变化自动将新值放到输入框中
            item.value = newVal
          })
          item.addEventListener("input",function(e) {
            // 输入框输入值时改变视图
            let newVal = e.target.value
            vm[value] = newVal
          });
        })
      }
      if (item.childNodes) {
        replace(item)
      }
    })
  }

  vm.$el.appendChild(fragment)
}

// 数据劫持
function Observe(data) {
  let dep = new Dep()
  console.log(data)
  for (let key in data) {
    // 旧值
    var val = data[key]
    console.log(val)

    observe(val);

    // Object.defineProperty 定义属性
    Object.defineProperty(data, key, {
      enumerable: true,
      get() {
        Dep.target && dep.addSub(Dep.target)  //[watcher]
        return val
      },
      set(newVal) {
        if (newVal == val) {
          return
        }
        val = newVal
        // 数据劫持
        console.log(newVal)
        observe(newVal)   //为了修改值后取到的新值也有get和set属性
        dep.notify()   //让所有watcher的update方法执行
      }
    })
  }
}

function observe(data) {
  if (typeof data !== 'object') {
    return
  }
  return new Observe(data)
}



// 发布订阅模式   先订阅（放函数） 后发布（执行函数）
function Dep() {
  // 数组池
  this.subs = []
}
// prototype定义方法
Dep.prototype.addSub = function (sub) {  //订阅
  // 将数组放入数组池
  this.subs.push(sub)
}
Dep.prototype.notify = function () {
  this.subs.forEach(sub => sub.update())
}

function Watcher(vm, exp, fn) {
  this.vm = vm
  this.exp = exp
  this.fn = fn
  // 将watcher添加到订阅中
  Dep.target = this
  console.log(Dep.target)   //Watcher {vm: Vue, exp: "a.a", fn: ƒ}
  let val = vm
  console.log(val)
  let arr = RegExp.$1.split('.')
  console.log(arr)
  arr.forEach(function (k) {  //this.a.a(调用get方法)
    console.log(k)
    val = val[k]
    console.log(val)
  })
  Dep.target = null
}
Watcher.prototype.update = function () {
  // 获取新值 
  let val = this.vm
  console.log(val)
  let arr = this.exp.split('.')
  console.log(arr)
  arr.forEach(function (k) {  //this.a.a(调用get方法)
    console.log(k)
    val = val[k]
    console.log(val)
  })
  // 执行函数
  this.fn(val)
}
// let watcher = new Watcher(function() {   //监听函数
//   console.log(1111)
// })
// let dep = new Dep()
// dep.addSub(watcher)   //将watcher放到数组中
// console.log(dep.subs)
// dep.notify()